<!--
 * @Description: 文件描述
 * @Author: 程前
 * @Date: 2024-12-27 10:42:54
 * @LastEditors: 程前
 * @LastEditTime: 2025-01-02 11:36:28
-->
<template>
    <div class="fly-container">
        <div class="handle-box"> <el-button width="100%" type="success" :icon="Plus" @click="addFly">新增飞行路径</el-button>
        </div>
        <div class="items-box">
            <div v-for="(tree, index) in flyList" :key="index" class="tree-item">
                <el-tree ref="treeRefs" :data="tree" accordion
                    @node-expand="(data: Tree, node: TreeChildren) => handleNodeExpand(data, node)"
                    @node-collapse="(data: Tree, node: TreeChildren) => handleNodeCollapse(data, node)">
                    <template #default="{ node, data }">
                        <!-- 父节点 -->
                        <div v-if="node.level === 1" class="tree-node">
                            <div v-if="!data.isEdit" class="tree-node-label">{{ data.label }}</div>
                            <el-input v-else v-model="data.label" @click.stop @blur="handleBlur(data)" />
                            <div class="icon-box">
                                <el-icon :size="iconSize" @click.stop="play(data)">
                                    <VideoPlay v-show="!data.isPlay" />
                                    <VideoPause v-show="data.isPlay" />
                                </el-icon>
                                <el-icon :size="iconSize" @click.stop="edit(node, data)">
                                    <Edit />
                                </el-icon>
                                <el-icon :size="iconSize" @click.stop="treeDelete(data)">
                                    <Delete />
                                </el-icon>
                            </div>
                        </div>
                        <!-- 视点 -->
                        <div v-else-if="data.data" class="tree-node-data">
                            <div class="fly-ponit-box">
                                <el-image class="fly-img" :src="data.data.shotImg" fit="fill" />
                                <div class="fly-config">
                                    <el-button type="primary" @click="refreshViewpoint(data)">重新获取</el-button>
                                    <div class="fly-config-bottom">
                                        时间: <el-input-number v-model="data.data.time" :min="0" @click.stop
                                            controls-position="right">
                                            <template #suffix>
                                                <span>s</span>
                                            </template>
                                        </el-input-number>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <!-- 子节点 -->
                        <div v-else class="tree-node">
                            <div v-if="!data.isEdit">{{ data.label }}</div>
                            <el-input v-else v-model="data.label" @click.stop @blur="handleBlur(data)" />
                            <div class="icon-box">
                                <el-icon :size="iconSize" @click.stop="location(data)">
                                    <Location />
                                </el-icon>
                                <el-icon :size="iconSize" @click.stop="edit(node, data)">
                                    <EditPen />
                                </el-icon>
                                <el-icon :size="iconSize" @click.stop="nodeDelete(node, data)">
                                    <CloseBold />
                                </el-icon>
                            </div>
                        </div>
                    </template>
                </el-tree>
                <div v-if="tree[0].type === 'add' && isNodeExpanded(tree[0].id)" class="add-viewpoint-btn">
                    <el-button @click="addCurrentViewpoint(tree[0])">
                        添加当前视点
                    </el-button>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, nextTick, reactive, isRef } from "vue"
import { Cartesian3 } from "cesium"
import { getCesiumScreenshot, fixedFreeCameraControl, freeCameraControl, disableCameraControl, customFlyTo, load3dTileset } from '@/utils'
import { useCesiumGlobalStore } from "@/store/modules/cesiun-global"
import { ElMessage } from 'element-plus'
import { Plus, VideoPlay, Edit, Delete, Location, EditPen, CloseBold, VideoPause } from '@element-plus/icons-vue'
import { ScrollbarDirKey } from "element-plus/es/components/virtual-list/src/defaults"

// 树形结构数据
interface Tree {
    id?: string | number;
    label: string;
    isEdit: boolean;
    type?: 'add' | 'local';
    data?: ShotData;
    isPlay: boolean;
    children?: TreeChildren
}
type TreeChildren = Omit<Tree, 'isPlay'>[]

interface ShotData {
    position?: Cartesian3;
    orientation?: {
        heading: number;
        pitch: number;
        roll: number;
    }
    time: number;
    shotImg: string;
};

/**
 * 配置数据
 */
const iconSize = 16
const cesiumGlobalStore = useCesiumGlobalStore()

/**
 * 新增飞行路径
 */
const flyList = ref<Tree[][]>([[
    {
        id: '1',
        label: '香港',
        isEdit: false,
        isPlay: false,
        type: 'local',
        children: [{
            id: "viewpoint-1735625483571-0",
            label: "视点 1",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625483571",
                label: "视点 1 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2417234.7862037825,
                        5387466.244149837,
                        2403885.614390137
                    ),
                    orientation: {
                        heading: 4.12841070065773,
                        pitch: -0.4511069569119588,
                        roll: 9.586519835735885e-7
                    },
                    time: 5,
                    shotImg: "1"
                }
            }]
        }, {
            id: "viewpoint-1735625483571-2",
            label: "视点 2",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625497225",
                label: "视点 2 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416834.157622402,
                        5387545.06013253,
                        2403538.006146134
                    ),
                    orientation: {
                        heading: 4.128410700657728,
                        pitch: -0.45110695691196145,
                        roll: 9.586519817972317e-7
                    },
                    time: 5,
                    shotImg: ""

                }
            }]
        }, {
            id: "viewpoint-1735625483571-4",
            label: "视点 4",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625520211",
                label: "视点 4 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416282.766433105,
                        5388156.430647497,
                        2403349.3445914774
                    ),
                    orientation: {
                        heading: 1.1934565009686695,
                        pitch: -0.6919627233730408,
                        roll: 0.000012022756362917164
                    },
                    time: 5,
                    shotImg: ""
                }
            }]
        }, {
            id: "viewpoint-1735625483571-5",
            label: "视点 5",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625528915",
                label: "视点 5 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416418.4968570606,
                        5387721.373014385,
                        2403285.584863163
                    ),
                    orientation: {
                        heading: 1.1715168757503047,
                        pitch: -0.2365834104225195,
                        roll: 6.283185221917363
                    },
                    time: 5,
                    shotImg: ""
                }
            }]
        }, {
            id: "viewpoint-1735625483571-6",
            label: "视点 6",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625545546",
                label: "视点 6 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416478.933077798,
                        5387657.200811231,
                        2403383.318794627
                    ),
                    orientation: {
                        heading: 1.7414295007189136,
                        pitch: -0.3082872007742883,
                        roll: 0.000006207848454842235
                    },
                    time: 5,
                    shotImg: ""
                }
            }]
        }, {
            id: "viewpoint-1735625483571-7",
            label: "视点 7",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625559892",
                label: "视点 7 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416609.5809866097,
                        5387617.922539335,
                        2403340.0047306544
                    ),
                    orientation: {
                        heading: 1.7414295117871594,
                        pitch: -0.3082871947852883,
                        roll: 0.000006171371039087603
                    },
                    time: 5,
                    shotImg: ""
                }
            }]
        }, {
            id: "viewpoint-1735625483571-8",
            label: "视点 8",
            isEdit: false,
            children: [{
                id: "viewpoint-data-1735625584981",
                label: "视点 8 数据",
                isEdit: false,
                data: {
                    position: new Cartesian3(
                        -2416795.065709661,
                        5387722.223656714,
                        2403167.528357104
                    ),
                    orientation: {
                        heading: 5.583914572861506,
                        pitch: -0.4287180226567413,
                        roll: 9.375972283010015e-7
                    },
                    time: 5,
                    shotImg: ""
                }
            }]
        }]
    }
]])


/**
 * 收起所有节点
 */
const treeRefs = ref([])
const collapseAll = () => {
    expandedNodes.value.clear()
    treeRefs.value.forEach(tree => {
        tree.store.root.childNodes.forEach(node => {
            node.collapse()
        })
    })
}

/**
 * 新增飞行路径
 */
const addFly = () => {
    const addNumber = flyList.value.filter(item => item[0].type === 'add').length + 1
    const newId = new Date().getTime().toString()
    const newNode: Tree = {
        id: newId,
        label: '飞行路线' + addNumber,
        isEdit: false,
        isPlay: false,
        type: 'add' as const,
        children: [{
            id: `viewpoint-${newId}-0`,
            label: '视点 1',
            isEdit: false,
            children: [{
                id: `viewpoint-data-${newId}`,
                label: '视点 1 数据',
                isEdit: false,
                data: reactive({
                    time: 5,
                    shotImg: '',
                    position: new Cartesian3(0, 0, 0)
                })
            }]
        }]
    }
    flyList.value.push([newNode])

    // 收起所有节点，然后展开新节点
    nextTick(() => {
        collapseAll()
        const lastTreeRef = treeRefs.value[treeRefs.value.length - 1]
        if (lastTreeRef) {
            lastTreeRef.store.root.childNodes[0].expand()
            expandedNodes.value.add(newId)
        }

        // 滚动到底部
        const itemsBox = document.querySelector('.items-box')
        if (itemsBox) {
            itemsBox.scrollTop = itemsBox.scrollHeight
        }
    })
}

/**
 * 删除指定飞行路线
 */
const treeDelete = (data: Tree) => {
    const treeIndex = flyList.value.findIndex(tree => {
        return tree.some(item => item.id === data.id)
    })
    if (treeIndex > -1) {
        flyList.value.splice(treeIndex, 1)
    }
}

/**
 * 播放飞行路径
 */
const currentPlayIndex = ref(0)
const currentPlayingPath = ref<Tree | null>(null)
const isPlaying = ref(false)

const play = async (data: Tree) => {
    if (data.type === 'local') {
        // 演示本地数据
        load3dTileset(cesiumGlobalStore.viewerInstance, '/public/3dtileset/hk/tileset.json')
        // return
    }
    if (!data.children[0].children[0].data.shotImg) {
        ElMessage.warning('该路径下没有视点数据!');
        return;
    }
    // 已播放路径
    if (currentPlayingPath.value === data) {
        if (data.isPlay) {
            data.isPlay = false;
            isPlaying.value = false;
            cesiumGlobalStore.viewerInstance.camera.cancelFlight();
            fixedFreeCameraControl(); //暂停时恢复交互
        } else {
            data.isPlay = true;
            isPlaying.value = true;
            // 从当前索引播放
            await continueFlight(data);
        }
        return;
    }

    // 新路径 重置后播放新路径
    if (currentPlayingPath.value) {
        currentPlayingPath.value.isPlay = false;
    }
    currentPlayIndex.value = 0;
    currentPlayingPath.value = data;
    data.isPlay = true;
    isPlaying.value = true;
    await continueFlight(data);
};

/**
 * 继续播放
 */
const continueFlight = async (data: Tree) => {
    try {
        cesiumGlobalStore.flyType === 'fixed' ? disableCameraControl() : freeCameraControl();
        for (let i = currentPlayIndex.value; i < data.children.length; i++) {
            if (!data.isPlay) break;
            const viewData = data.children[i].children?.[0]?.data;

            if (!viewData?.position) continue;
            currentPlayIndex.value = i;

            try {
                await flyToPosition(viewData);
            } catch (error) {
                if (error.message === '取消飞行') {
                    return;
                }
                console.error('飞行到视点失败:', error);
                break;
            }
        }

        // 播放完成重置所有状态
        currentPlayIndex.value = 0;
        currentPlayingPath.value = null;
        data.isPlay = false;
        isPlaying.value = false;
        fixedFreeCameraControl();
    } catch (error) {
        console.error('飞行出错:', error);
    }
};

/**
 * 单点飞行函数
 * @param viewData 
 */
const flyToPosition = (viewData: ShotData) => {
    return new Promise((resolve, reject) => {
        const Viewer = cesiumGlobalStore.viewerInstance;
        let cancelFlight: (() => void) | null = null;
        // 设置相机控制
        cesiumGlobalStore.flyType === 'fixed' ? disableCameraControl() : freeCameraControl;
        try {
            cancelFlight = customFlyTo(Viewer, {
                destination: viewData.position,
                orientation: cesiumGlobalStore.flyType === 'fixed' ? viewData.orientation : undefined,
                duration: viewData.time,
                onComplete: () => {
                    cesiumGlobalStore.flyType === 'fixed' && fixedFreeCameraControl();
                    resolve(true);
                },
                onCancel: () => {
                    cesiumGlobalStore.flyType === 'fixed' && fixedFreeCameraControl();
                    reject(new Error('取消飞行'));
                }
            });

            // 保存取消函数到 viewer，以便可以从外部取消
            Viewer.camera.cancelFlight = () => {
                cancelFlight?.();
            };
        } catch (error) {
            reject(error);
        }
    });
};

/**
 * 编辑
 */
const edit = (node, data: Tree) => {
    data.isEdit = !data.isEdit
}


/**
 * 处理输入框失焦事件
 */
const handleBlur = (data: Tree) => {
    data.isEdit = false
    if (!data.label.trim()) {
        data.label = '未命名路径'
    }
}

/**
 * 新增视点
 * @param node 当前节点
 */
const addCurrentViewpoint = async (node: Tree) => {
    try {
        const Viewer = cesiumGlobalStore.viewerInstance
        const shotImg = await getCesiumScreenshot(Viewer);
        const camera = Viewer.camera;
        const viewpointData: ShotData = reactive({
            position: camera.position.clone(),
            orientation: {
                heading: camera.heading,
                pitch: camera.pitch,
                roll: camera.roll
            },
            time: 5,
            shotImg: shotImg
        });

        // 是否空视点 是则更新
        if (node.children && node.children.length === 1 && !node.children[0].children?.[0]?.data?.shotImg) {
            if (node.children[0].children?.[0]) {
                node.children[0].children[0].data = viewpointData;
            }
        } else {
            // 否则添加
            if (!node.children) {
                node.children = [];
            }
            const nodeLength = node.children.length + 1
            const viewpointNode: Omit<Tree, 'isPlay'> = {
                id: `viewpoint-${node.id}-${nodeLength}`,
                label: `视点 ${nodeLength}`,
                isEdit: false,
                children: [{
                    id: `viewpoint-data-${Date.now()}`,
                    label: `视点 ${nodeLength} 数据`,
                    isEdit: false,
                    data: viewpointData
                }]
            };
            node.children.push(viewpointNode);
        }
        await nextTick()
        // 找到当前实例树
        const currentTree = treeRefs.value.find(tree =>
            tree.store.root.childNodes[0].data.id === node.id
        )
        if (currentTree) {
            currentTree.store.root.childNodes[0].expand()
            if (node.id) {
                expandedNodes.value.add(node.id.toString())
            }
            await nextTick()
            // 展开新增节点
            const lastChild = currentTree.store.root.childNodes[0].childNodes.at(-1)
            if (lastChild) {
                lastChild.expand()
                setTimeout(() => {
                    const itemsBox = document.querySelector('.items-box')
                    if (itemsBox) {
                        itemsBox.scrollTo({
                            top: itemsBox.scrollHeight,
                            behavior: 'smooth'
                        })
                    }
                }, 150)
            }
        }
        ElMessage.success('视点添加成功');
    } catch (error) {
        console.error('添加视点失败:', error);
        ElMessage.error('添加视点失败');
    }
}

/**
 * 删除指定视点
 */
const nodeDelete = (node: any, data: Tree) => {
    const parentNode = node.parent.data
    if (parentNode.children) {
        const currentViewpointNumber = data.id?.toString().split('-').pop()
        const viewpointIndex = parentNode.children.findIndex((child: Tree) => {
            const childNumber = child.id?.toString().split('-').pop()
            return childNumber === currentViewpointNumber
        })
        if (viewpointIndex > -1) {
            parentNode.children.splice(viewpointIndex, 1)
            ElMessage.success('视点删除成功')
        }
    }
}

/**
 * 提取父节点id
 * @param viewpointString 
 */
const extractParentId = (viewpointString: string) => {
    const regex = /viewpoint-(\d+)-\d+/;
    const match = viewpointString.match(regex);
    return match ? match[1] : null;
}

/**
 * 定位到指定视点
 */
const location = (data: Tree) => {
    const viewData = data.children[0].data
    if (viewData && viewData.position && viewData.orientation) {
        const Viewer = cesiumGlobalStore.viewerInstance
        Viewer.camera.flyTo({
            destination: viewData.position,
            orientation: {
                heading: viewData.orientation.heading,
                pitch: viewData.orientation.pitch,
                roll: viewData.orientation.roll
            },
            duration: 3,

        })
    }
}

/**
 * 重新获取视点信息
 * @param data 前节点数据
 */
const refreshViewpoint = async (data: Tree) => {
    try {
        const Viewer = cesiumGlobalStore.viewerInstance
        const shotImg = await getCesiumScreenshot(Viewer);
        const camera = Viewer.camera;
        if (data.data) {
            data.data.shotImg = shotImg;
            data.data.position = new Cartesian3(camera.position.x, camera.position.y, camera.position.z);
            data.data.orientation = {
                heading: camera.heading,
                pitch: camera.pitch,
                roll: camera.roll
            };
            ElMessage.success('视点更新成功');
        }
    } catch (error) {
        console.error('更新视点失败:', error);
        ElMessage.error('更新视点失败');
    }
}

/**
 * 节点展开处理函数
 */
const expandedNodes = ref<Set<string>>(new Set())
const handleNodeExpand = (data: Tree, node: any) => {
    if (data.id && node.level === 1) {
        expandedNodes.value.clear()
        treeRefs.value.forEach(tree => {
            const rootNode = tree.store.root.childNodes[0]
            if (rootNode.data.id !== data.id) {
                rootNode.collapse()
            }
        })
        expandedNodes.value.add(data.id.toString())
    }
}

/**
 * 节点收起处理函数
 */
const handleNodeCollapse = (data: Tree, node: any) => {
    if (data.id && node.level === 1) {
        expandedNodes.value.delete(data.id.toString())
    }
}

/**
 * 判断节点是否展开
 * @param nodeId 
 */
const isNodeExpanded = (nodeId: string | number | undefined) => {
    if (!nodeId) return false
    return expandedNodes.value.has(nodeId.toString())
}

</script>

<style scoped lang="less">
.fly-container {
    width: 100%;

    .handle-box {
        width: fit-content;
        margin: 0 auto;
    }

    .items-box {
        margin-top: 12px;
        overflow-y: auto;
        height: 300px;

        &::-webkit-scrollbar {
            display: none;
        }

        .tree-item {
            margin-bottom: 16px;
        }

        .add-viewpoint-btn {
            display: flex;
            justify-content: center;
            margin-top: 12px;

            .el-button {
                margin-top: 2px;
                box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.1);
            }
        }

        .tree-node {
            display: flex;
            justify-content: space-between;
            width: 100%;
            font-size: 16px;

            .icon-box {
                display: flex;
                align-items: center;

                i {
                    margin: 0 4px;
                    z-index: 9;
                }

                i:hover {
                    color: rgb(89, 160, 241);
                }
            }

            .el-input {
                width: 135px;
                font-size: 16px;
            }

            .tree-node-label {
                width: 135px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }

        }

        .tree-node-data {
            position: relative;
            right: 36px;

            .fly-ponit-box {
                display: flex;
                justify-content: space-between;
                padding: 4px 0;

                .fly-img {
                    width: 100px;
                    height: 80px;
                }

                .fly-config {
                    margin-left: 16px;

                    .el-button {
                        margin-left: 36px;
                    }

                    .fly-config-bottom {
                        margin-top: 14px;
                    }
                }
            }
        }
    }
}

:deep(.el-tree-node__content) {
    height: 100%;
    padding: 6px 0;
}

.el-input-number {
    width: 90px;
}
</style>